package algorythm;

import java.awt.Point;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Map;

public class Piece 
{
	private Point position;
	private PieceType type;
	private Color color;

	public enum PieceType 
	{
		KNIGHT,
		BISHOP,
		KING,
		QUEEN,
		TOWER,
		PAWN;

		@Override
		public String toString() 
		{
			switch (this) 
			{
			case KNIGHT:
				return "Knight";
			case BISHOP:
				return "Bishop";
			case QUEEN:
				return "Queen";
			case KING:
				return "King";
			case TOWER:
				return "Tower";
			case PAWN:
				return "Pawn";
			}
			return null;
		}
	}

	public enum Color 
	{
		WHITE,
		BLACK;

		@Override
		public String toString() 
		{
			switch (this) 
			{
			case BLACK:
				return "Black";
			case WHITE:
				return "White";
			}
			return null;
		}

		public Color getEnemyColor()
		{
			if(this == WHITE)
			{
				return BLACK;
			}
			return WHITE;
		}
	}

	public Piece(Point position, PieceType type, Color color) 
	{
		this.position = position;
		this.type = type;
		this.color = color;
	}

	public Point getPosition() 
	{
		return position;
	}

	public PieceType getType() 
	{
		return type;
	}

	public Color getColor() 
	{
		return color;
	}

	public List<Point> possibleMoves(Map<Point, Piece> boardPositions, Piece piece, Point piecePosition) 
	{
		switch (type) 
		{
		case PAWN:
			return Piece.getPawnMoves(piece, boardPositions,piecePosition);
		case KNIGHT:
			return Piece.getKnigthMoves(piece, boardPositions,piecePosition);
		case BISHOP:
			return getBishopMoves(piece, boardPositions,piecePosition);
		case TOWER:
			return getTowerMoves(piece, boardPositions,piecePosition);
		case KING:
			return getKingMoves(piece, boardPositions,piecePosition);
		case QUEEN:			
			List<Point> temporalCoordinates = new ArrayList<>();
			temporalCoordinates = getTowerMoves(piece, boardPositions,piecePosition);
			temporalCoordinates.addAll(getBishopMoves(piece, boardPositions,piecePosition));
			return temporalCoordinates;
		default:
			System.err.println("Al parecer alguien ha modificado el codigo ilegalmente");
			break;
		}
		return null;
	}

	private static List<Point> getPawnMoves(Piece pawn, Map<Point, Piece> boardPositions,Point piecePosition) 
	{
		if (pawn.type != PieceType.PAWN) 
		{
			throw new InputMismatchException("This method has to receive a piece of type Pawn");
		}
		ArrayList<Point> possibleMoves = new ArrayList<Point>();
		short modifier = 0;
		boolean doubleMove = false;
		if (pawn.color == Color.WHITE) 
		{
			doubleMove = piecePosition.y == 6; 
			modifier = -1;
		} 
		else 
		{
			doubleMove = piecePosition.y == 1;
			modifier = 1;
		}
		Point coordinate = new Point(piecePosition.x, piecePosition.y + modifier);
		if (boardPositions.get(coordinate) == null && isCoordinateInBoard(coordinate)) 
		{
			possibleMoves.add(coordinate);
			coordinate = new Point(piecePosition.x, piecePosition.y + (modifier * 2));
			if (doubleMove && boardPositions.get(coordinate) == null && isCoordinateInBoard(coordinate)) 
			{
				possibleMoves.add(coordinate);
			}
		}
		coordinate = new Point(piecePosition.x - 1, piecePosition.y + modifier);
		Piece diagonalPiece = boardPositions.get(coordinate);
		if (diagonalPiece != null && diagonalPiece.color != pawn.color && isCoordinateInBoard(coordinate)) 
		{
			possibleMoves.add(coordinate);
		}
		coordinate = new Point(piecePosition.x + 1, piecePosition.y + modifier);
		diagonalPiece = boardPositions.get(coordinate);
		if (diagonalPiece != null && diagonalPiece.color != pawn.color && isCoordinateInBoard(coordinate)) 
		{
			possibleMoves.add(coordinate);
		}
		return possibleMoves;
	}

	private static List<Point> getKnigthMoves(Piece knight, Map<Point, Piece> boardPositions,Point piecePosition) 
	{
		if (knight.type != PieceType.KNIGHT) 
		{
			throw new InputMismatchException("This method has to receive a piece of type Knight");
		}
		ArrayList<Point> possibleMoves = new ArrayList<Point>();
		Point coordinate = new Point(piecePosition.x - 1, piecePosition.y - 2); //UL
		if (isCoordinateInBoard(coordinate)) 
		{
			Piece pieceToEat = boardPositions.get(coordinate);
			if(pieceToEat == null || pieceToEat.color != knight.color)
			{
				possibleMoves.add(coordinate);
			}
		}
		coordinate = new Point(piecePosition.x + 1, piecePosition.y - 2);		//UR
		if (isCoordinateInBoard(coordinate)) 
		{
			Piece pieceToEat = boardPositions.get(coordinate);
			if(pieceToEat == null || pieceToEat.color != knight.color)
			{
				possibleMoves.add(coordinate);
			}
		}
		coordinate = new Point(piecePosition.x - 1, piecePosition.y + 2);		//DL
		if (isCoordinateInBoard(coordinate)) 
		{
			Piece pieceToEat = boardPositions.get(coordinate);
			if(pieceToEat == null || pieceToEat.color != knight.color)
			{
				possibleMoves.add(coordinate);
			}
		}
		coordinate = new Point(piecePosition.x + 1, piecePosition.y + 2);		//DR
		if (isCoordinateInBoard(coordinate)) 
		{
			Piece pieceToEat = boardPositions.get(coordinate);
			if(pieceToEat == null || pieceToEat.color != knight.color)
			{
				possibleMoves.add(coordinate);
			}
		}
		coordinate = new Point(piecePosition.x + 2, piecePosition.y - 1);		//RU
		if (isCoordinateInBoard(coordinate)) 
		{
			Piece pieceToEat = boardPositions.get(coordinate);
			if(pieceToEat == null || pieceToEat.color != knight.color)
			{
				possibleMoves.add(coordinate);
			}
		}
		coordinate = new Point(piecePosition.x + 2, piecePosition.y + 1);		//RD
		if (isCoordinateInBoard(coordinate)) 
		{
			Piece pieceToEat = boardPositions.get(coordinate);
			if(pieceToEat == null || pieceToEat.color != knight.color)
			{
				possibleMoves.add(coordinate);
			}
		}
		coordinate = new Point(piecePosition.x - 2, piecePosition.y - 1);		//LU
		if (isCoordinateInBoard(coordinate)) 
		{
			Piece pieceToEat = boardPositions.get(coordinate);
			if(pieceToEat == null || pieceToEat.color != knight.color)
			{
				possibleMoves.add(coordinate);
			}
		}
		coordinate = new Point(piecePosition.x - 2, piecePosition.y + 1);		//LD
		if (isCoordinateInBoard(coordinate)) 
		{
			Piece pieceToEat = boardPositions.get(coordinate);
			if(pieceToEat == null || pieceToEat.color != knight.color)
			{
				possibleMoves.add(coordinate);
			}
		}
		return possibleMoves;
	}

	private  List<Point> getBishopMoves(Piece bishop, Map<Point, Piece> boardPositions,Point piecePosition) {
		if (bishop.type != PieceType.BISHOP && bishop.type != PieceType.QUEEN) 
		{
			throw new InputMismatchException("This method has to receive a piece of type Bishop");
		}
		ArrayList<Point> possibleMoves = new ArrayList<Point>();
		boolean 
		searchUL = true,
		searchUR = true,
		searchDL = true,
		searchDR = true;
		Point p;
		Piece pieceBlocking;
		for (int i = 1; ((i < 8) && (searchUL || searchUR || searchDL || searchDR)); i++) 
		{
			if (searchUL) 
			{
				p = new Point(piecePosition.x - i,piecePosition.y + i);
				if (isCoordinateInBoard(p)) 
				{
					if ((pieceBlocking = boardPositions.get(p)) == null || pieceBlocking.color != bishop.color) 
					{
						possibleMoves.add(p);
					}
					if(pieceBlocking != null)
					{
						searchUL = false;
					}
				}
				else
				{
					searchUL = false;
				}
			}
			if (searchUR) 
			{
				p = new Point(piecePosition.x + i, piecePosition.y + i);
				if (isCoordinateInBoard(p)) 
				{
					if ((pieceBlocking = boardPositions.get(p)) == null || pieceBlocking.color != bishop.color) 
					{
						possibleMoves.add(p);
					}
					if(pieceBlocking != null)
					{
						searchUR = false;
					}
				}
				else
				{
					searchUR = false;
				}
			}
			if (searchDL) 
			{
				p = new Point(piecePosition.x - i,piecePosition.y - i);
				if (isCoordinateInBoard(p)) 
				{
					if ((pieceBlocking = boardPositions.get(p)) == null || pieceBlocking.color != bishop.color) 
					{
						possibleMoves.add(p);
					}
					if(pieceBlocking != null)
					{
						searchDL = false;
					}
				}
				else
				{
					searchDL = false;
				}
			}
			if (searchDR) 
			{
				p = new Point(piecePosition.x + i, piecePosition.y - i);
				if (isCoordinateInBoard(p)) 
				{
					if ((pieceBlocking = boardPositions.get(p)) == null || pieceBlocking.color != bishop.color) 
					{
						possibleMoves.add(p);
					}
					if(pieceBlocking != null)
					{
						searchDR = false;
					}
				}
				else
				{
					searchDR = false;
				}
			}
		}
		return possibleMoves;
	}

	private static List<Point> getTowerMoves(Piece tower, Map<Point, Piece> boardPositions,Point piecePosition) {
		if (tower.type != PieceType.TOWER && tower.type != PieceType.QUEEN) 
		{
			throw new InputMismatchException("This method has to receive a piece of type tower");
		}
		ArrayList<Point> possibleMoves = new ArrayList<Point>();
		boolean 
		searchU = true,
		searchD = true,
		searchL = true,
		searchR = true;
		Point p;
		Piece pieceBlocking;
		for (int i = 1; ((i < 8) && (searchU || searchD || searchL || searchR)); i++) 
		{
			if (searchU) 
			{
				p = new Point(piecePosition.x, piecePosition.y - i);
				if (isCoordinateInBoard(p)) 
				{
					if ((pieceBlocking = boardPositions.get(p)) == null || pieceBlocking.color != tower.color) 
					{
						possibleMoves.add(p);
					}
					if(pieceBlocking != null)
					{
						searchU = false;
					}
				}
				else
				{
					searchU = false;
				}
			}
			if (searchD) 
			{
				p = new Point(piecePosition.x, piecePosition.y + i);
				if (isCoordinateInBoard(p)) 
				{
					if ((pieceBlocking = boardPositions.get(p)) == null || pieceBlocking.color != tower.color) 
					{
						possibleMoves.add(p);
					}
					if(pieceBlocking != null)
					{
						searchD = false;
					}
				}
				else
				{
					searchD = false;
				}
			}
			if (searchL) 
			{
				p = new Point(piecePosition.x - i, piecePosition.y);
				if (isCoordinateInBoard(p)) 
				{
					if ((pieceBlocking = boardPositions.get(p)) == null || pieceBlocking.color != tower.color) 
					{
						possibleMoves.add(p);
					}
					if(pieceBlocking != null)
					{
						searchL = false;
					}
				}
				else
				{
					searchL = false;
				}
			}
			if (searchR) 
			{
				p = new Point(piecePosition.x + i, piecePosition.y);
				if (isCoordinateInBoard(p)) 
				{
					if ((pieceBlocking = boardPositions.get(p)) == null || pieceBlocking.color != tower.color) 
					{
						possibleMoves.add(p);
					}
					if(pieceBlocking != null)
					{
						searchR = false;
					}
				}
				else
				{
					searchR = false;
				}
			}
		}
		return possibleMoves;
	}

	private static List<Point> getKingMoves(Piece king, Map<Point, Piece> boardPositions,Point piecePosition) 
	{
		if (king.type != PieceType.KING) 
		{
			throw new InputMismatchException("This method has to receive a piece of type King");
		}
		ArrayList<Point> possibleMoves = new ArrayList<Point>();
		Point coordinate;
		Piece pieceToEat;
		for (short i = -1; i < 2; i++) 
		{
			if (i + piecePosition.x < 0 || i + piecePosition.x > 7) 
			{
				continue;
			}
			for (short j = -1; j < 2; j++) {
				if (j + piecePosition.y < 0 || j + piecePosition.y > 7) 
				{
					continue;
				} 
				if (i == j && i == 0)
				{
					continue;
				}

				coordinate = new Point(piecePosition.x + i, piecePosition.y + j);
				if ((pieceToEat = boardPositions.get(coordinate)) == null || pieceToEat.color != king.color) 
				{
					possibleMoves.add(coordinate);
				}
			}
		}
		return possibleMoves;
	}

	private static boolean isCoordinateInBoard(Point coordinate) 
	{
		return (coordinate.x < 8 && coordinate.x >= 0) && (coordinate.y < 8 && coordinate.y >= 0);
	}

	public void changeTypeOfPiece(PieceType newType) {
		type = newType;
	}

	public void setPosition(Point position) 
	{
		this.position = position;
	}

	public int getValue()
	{
		if(this.type == PieceType.BISHOP)
		{
			//System.out.println("Bishop");
			return 3;
		}
		if(this.type == PieceType.TOWER)
		{
			//System.out.println("tower");
			return 5;
		}
		if(this.type == PieceType.KNIGHT)
		{
			//System.out.println("knight");
			return 3;
		}
		if(this.type == PieceType.QUEEN)
		{
			//System.out.println("queen");
			return 9;
		}
		if(this.type == PieceType.PAWN)
		{
			//System.out.println("pawn");
			return 1;
		}
		if(this.type == PieceType.KING)
		{
			//System.out.println("king");
			return 999;
		}
		//System.out.println("Sin tipo");
		return 0;
	}

	@Override
	public String toString() 
	{
		return "Tipo de Pieza: "+type+"\nColor: "+color+"\nPosision: "+position;
	}
}
